package net.mindView.concurrency;


import jdk.nashorn.internal.ir.CatchNode;
import net.mindview.util.Enums;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

public class RestaurantWithQueue {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        Restaurant restaurant = new Restaurant(exec,5,2);
        exec.execute(restaurant);
        TimeUnit.MILLISECONDS.sleep(3600);
        exec.shutdownNow();
    }
}

interface Food{
    enum Appetizer implements Food{
        SALAD,SOUP,SPRING_ROLLS;
    }
    enum MainCourse implements Food{
        LASAGNE,BURRITO,PAD_THAI,
        LENTILS,HUMMOUS,VINDALOO;
    }
    enum Dessert implements Food{
        TIRAMISU,GELATO,BLACK_FOREST_CAKE,
        FRUIT,CREME_CARAMEL;
    }
    enum Coffee implements Food{
        BLACK_COFFEE,DECAF_COFFEE,ESPRESSO,
        LATTE,CAPPUCCINO,TEA,HERB_TEA;
    }
}

//随机生成食物
enum Course{
    APPETIZER(Food.Appetizer.class),
    MAINCOURSE(Food.MainCourse.class),
    DESSERT(Food.Dessert.class),
    COFFEE(Food.Coffee.class);
    private Food[] values;
    private Course(Class<? extends Food> kind){
        values = kind.getEnumConstants();
    }
    public Food randomSelection(){
        return Enums.random(values);
    }
}

//菜单，交给服务员，服务员再交给厨师
class Order{
    private static int counter = 0;
    private final int id = counter++;
    private final Customer2 customer;
    private final WaitPerson2 waitPerson;
    private final Food food;
    public Order(Customer2 ct,WaitPerson2 wp,Food f){
        customer = ct;
        waitPerson = wp;
        food = f;
    }
    public Food item(){ return food; }
    public Customer2 getCustomer(){ return customer; }

    public WaitPerson2 getWaitPerson() { return waitPerson; }
}

//盘子，从厨师那里得到
class Plate{
    private final Order order;
    private final Food food;
    public Plate(Order ord,Food f){
        order = ord;
        food = f;
    }
    public Order getOrder(){ return order; }
    public Food getFood(){ return food; }
    public String toString(){ return food.toString(); }
}

//顾客
class Customer2 implements Runnable{
    private static int counter = 0;
    private final int id = counter++;
    private final WaitPerson2 waitPerson;

    //SynchronousQueue一种没有内部容量的阻塞队列，因此每个take()必须等待一个put()
    //这就好像你在把一个对象交给某人—没有任何桌子可以放置这个对象，因此只有在这个人伸出手，
    //准备好接收这个对象时，你才能工作
    private SynchronousQueue<Plate> placeSetting =
            new SynchronousQueue<Plate>();
    public Customer2(WaitPerson2 waitPerson){ this.waitPerson = waitPerson; }

    public void deliver(Plate plate) throws InterruptedException{
        placeSetting.put(plate);//顾客还没有拿走则会阻塞
    }

    @Override
    public void run() {
        for(Course course: Course.values()){
            Food food = course.randomSelection();
            try {
                waitPerson.placeOrder(this,food);
                //顾客获取服务员提供的菜
                System.out.println(this + " eating " + placeSetting.take());
            }catch (InterruptedException e){
                System.out.println(this + " waiting for " + course + " interrupted");
                break;
            }
        }
        System.out.println(this + " finished meal,leaving");
    }

    public String toString(){ return "Customer " + id + " "; }
}

class WaitPerson2 implements Runnable{
    private static int counter = 0;
    private final int id = counter++;
    private final Restaurant restaurant;
    BlockingQueue<Plate> filledOrders =
            new LinkedBlockingQueue<Plate>();
    public WaitPerson2(Restaurant restaurant){ this.restaurant = restaurant; }

    //提交菜单给餐馆
    public void placeOrder(Customer2 cust,Food food){
        try {
            restaurant.orders.put(new Order(cust,this,food));
        }catch (InterruptedException e){
            System.out.println(this + " placeOrder interrupted");
        }
    }

    @Override
    public void run() {
        try{
            while (!Thread.interrupted()){
                Plate plate = filledOrders.take();
                System.out.println(this + " received " + plate +
                        " delivering to " + plate.getOrder().getCustomer());
                //将菜交给顾客
                plate.getOrder().getCustomer().deliver(plate);
            }
        }catch (InterruptedException e){
            System.out.println(this + " interrupted");
        }
        System.out.println(this + " off duty");
    }

    @Override
    public String toString() {
        return "WaitPerson " + id +" ";
    }
}

class Chef implements Runnable{
    private static int counter = 0;
    private final int id = counter++;
    private Restaurant restaurant;
    private static Random rand = new Random(47);
    public Chef(Restaurant rest){ restaurant = rest; }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()){
                //从餐馆获取菜单
                Order order = restaurant.orders.take();
                Food requestedItem = order.item();
                TimeUnit.MILLISECONDS.sleep(rand.nextInt(500));
                //将菜装进盘子
                Plate plate = new Plate(order,requestedItem);
                order.getWaitPerson().filledOrders.put(plate);
            }
        }catch (InterruptedException e){
            System.out.println(this + " interrupted");
        }
        System.out.println(this + " off duty");
    }

    public String toString(){ return "Chef " + id + " "; }
}

class Restaurant implements Runnable{
    private List<WaitPerson2> waitPersons =
            new ArrayList<WaitPerson2>();
    private List<Chef> chefs = new ArrayList<Chef>();
    private ExecutorService exec;
    private static Random rand = new Random(47);
    BlockingQueue<Order> orders = new LinkedBlockingQueue<Order>();

    public Restaurant(ExecutorService e,int nWaitPersons,int nChefs){
        exec = e;
        for(int i=0;i<nWaitPersons;i++){
            WaitPerson2 waitPerson = new WaitPerson2(this);
            waitPersons.add(waitPerson);
            exec.execute(waitPerson);
        }
        for(int i=0;i<nChefs;i++){
            Chef chef = new Chef(this);
            chefs.add(chef);
            exec.execute(chef);
        }
    }

    @Override
    public void run() {
        try{
            while(!Thread.interrupted()){
                WaitPerson2 wp = waitPersons.get(rand.nextInt(waitPersons.size()));
                Customer2 customer = new Customer2(wp);
                exec.execute(customer);
                TimeUnit.MILLISECONDS.sleep(100);
            }
        }catch (InterruptedException e){
            System.out.println("Restaurant interrupted");
        }
        System.out.println("Restaurant closing");
    }
}
